package org.example.sunrise.views.models;

import javafx.scene.canvas.GraphicsContext;
import javafx.scene.image.Image;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.stage.WindowEvent;
import org.example.sunrise.core.Sprite;
import org.example.sunrise.core.SpriteEnum;
import org.example.sunrise.domains.Coordinate;
import org.example.sunrise.domains.SpriteInfo;
import org.example.sunrise.models.LocalImageModel;
import org.example.sunrise.models.LocalSpriteModel;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

public class LocalGameViewModel {
    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(10);
    private final LocalSpriteModel localSpriteModel = new LocalSpriteModel();
    private final LocalImageModel localImageModel = new LocalImageModel();
    private final List<Sprite> sprites = new ArrayList<>();
    private Sprite chosen;
    private int sunValues;
    private final SpriteInfo sunInfo = localSpriteModel.get(SpriteEnum.SUN);
    private final Image sunImage = localImageModel.get(new LocalImageModel.ImageKey(sunInfo.resource(), sunInfo.scale().x(), sunInfo.scale().y(), LocalGameViewModel.class));

    public LocalGameViewModel() {
        initialize();
    }

    private void initialize() {
        sprites.add(createPlantChosen(156, 9, Sprite::new));
        localSpriteModel.get(SpriteEnum.SUN);
        localSpriteModel.get(SpriteEnum.SUNFLOWER);
    }

    public void render(GraphicsContext gc) {
        renderSprites(gc);
        renderSunValues(gc);
    }

    private void renderSprites(GraphicsContext gc) {
        synchronized (sprites) {
            sprites.removeIf(Sprite::isNeedDestroy);
            sprites.forEach(sprite -> {
                sprite.onUpdate();
                gc.save();
                gc.translate(sprite.getPosition().x(), sprite.getPosition().y());
                if (sprite.getImage() != null) {
                    gc.drawImage(sprite.getImage(), 0, 0);
                }
                gc.restore();
            });
        }
    }

    private void renderSunValues(GraphicsContext gc) {
        gc.setFont(new Font(20));
        gc.setFill(Color.LIGHTGOLDENRODYELLOW);
        gc.fillRect(21, 60, 56, 21);
        gc.setFill(Color.BLACK);
        gc.fillText(String.valueOf(sunValues), 33, 78, 37);
    }

    public void runTask() {
        generateSun(null);
        changeAppearance();
    }

    private ScheduledFuture<?> generateSun(Sprite sprite) {
        Optional<Sprite> sunflower = Optional.ofNullable(sprite);
        return scheduler.scheduleAtFixedRate(() -> {
            Sprite sun = new Sprite();
            sunflower.ifPresent(s -> {
                sun.setPosition(s.getPosition());
                s.setBound(s.getPosition());
            });
            sun.setOnMouseClicked(s -> {
                s.setNeedDestroy(true);
                sunValues += 25;
            });
            synchronized (sprites) {
                sprites.add(sun);
            }
            scheduler.schedule(() -> sun.setNeedDestroy(true), 6, TimeUnit.SECONDS);
        }, 2, 10, TimeUnit.SECONDS);
    }

    private void changeAppearance() {
        scheduler.scheduleAtFixedRate(() -> {
            synchronized (sprites) {
                sprites.forEach(Sprite::changeAppearance);
            }
        }, 0, 100, TimeUnit.MILLISECONDS);
    }

    public void stopTask(WindowEvent evt) {
        scheduler.shutdownNow();
    }

    public void onMouseMoved(MouseEvent evt) {
        if (chosen != null) {
            chosen.setPosition(new Coordinate(evt.getX() - chosen.getScale().x() / 2, evt.getY() - chosen.getScale().y() / 2));
        }
    }

    public void onMouseClicked(MouseEvent evt) {
        final double x = evt.getX();
        final double y = evt.getY();
        synchronized (sprites) {
            sprites.forEach(sprite -> {
                if (sprite.isChosen(x, y)) {
                    sprite.onMouseClicked();
                }
                if (chosen == sprite) {
                    chosen.onReady();
                    chosen = null;
                }
            });
            if (chosen != null && !sprites.contains(chosen)) {
                sprites.add(chosen);
            }
        }
    }

    public Sprite createPlantChosen(double x, double y, Supplier<Sprite> supplier) {
        Sprite sprite = new Sprite();
        sprite.setPosition(new Coordinate(x, y));
        sprite.setScale(new Coordinate(48, 68));
        sprite.setOnMouseClicked(s -> {
            chosen = supplier.get();
            chosen.setPosition(s.getPosition());
            chosen.setOnReady(ss -> ss.setFuture(generateSun(ss)));
        });
        return sprite;
    }
}
